package Graphique;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.HashMap;

public class RotatingImage extends BufferedImage {
private BufferedImage sourceImage;
 
private double angle;
 
private int numberOfImages;
 
private int sourceWidth;
 
private int sourceHeight;
 
private HashMap<Integer, BufferedImage> images;
 
public RotatingImage(BufferedImage sourceImage, boolean preCalc, int numberOfImages) {
	super((int) Math.sqrt(sourceImage.getWidth() * sourceImage.getWidth() + sourceImage.getHeight() * sourceImage.getHeight()),
	(int) Math.sqrt(sourceImage.getWidth() * sourceImage.getWidth() + sourceImage.getHeight() * sourceImage.getHeight()),
	BufferedImage.TYPE_INT_ARGB);
	this.sourceImage = sourceImage;
	this.sourceWidth = sourceImage.getWidth();
	this.sourceHeight = sourceImage.getHeight();
	this.angle = 0;
	this.numberOfImages = numberOfImages;
	this.images = new HashMap<Integer, BufferedImage>();
	if (preCalc)
		preCalculateImages();
 
}
 
public RotatingImage(BufferedImage sourceImage, boolean preCalc) {
	this(sourceImage, preCalc, 36);
}
 
private void preCalculateImages() {
	for (int i = 0; i < numberOfImages; i++) {
		setAngle(i * (2 * Math.PI) / numberOfImages);
		rotate();
	}
}
 
public RotatingImage(BufferedImage sourceImage) {
	this(sourceImage, false);
}
 
/**
* Rotate the source image
*
*/
private void rotate() {
	double hypotenuseLength = Math.sqrt(sourceWidth * sourceWidth + sourceHeight * sourceHeight);
	int rotatedWidth = (int) hypotenuseLength + 1;
	int rotatedHeight = (int) hypotenuseLength + 1;
	 
	BufferedImage rotatedImage = new BufferedImage(rotatedWidth, rotatedHeight, BufferedImage.TYPE_INT_ARGB);
	 
	int x = -1;
	int y = -1;
	int sector = (int) (Math.round(angle / ((2 * Math.PI) / (double) numberOfImages)));
	if (images.get(new Integer(sector)) != null) {
		rotatedImage = (BufferedImage) images.get(new Integer(sector));
	} else {
		for (int xRot = 0; xRot < rotatedWidth; xRot++) {
			for (int yRot = 0; yRot < rotatedHeight; yRot++) {
				Point xyCoord = getSourceXY(xRot - (rotatedWidth / 2), yRot - (rotatedHeight / 2));
				x = xyCoord.x;
				y = xyCoord.y;
				if (x >= 0 && x < sourceWidth && y >= 0 && y < sourceHeight) {
					int rgbColor = sourceImage.getRGB(x, y);
					try {
						rotatedImage.setRGB(xRot, yRot, rgbColor);
					} catch (Exception e) {
						System.out.print(" => Error");
					}
				}
			}
		}
		images.put(new Integer(sector), rotatedImage);
	}
	Graphics2D g = (Graphics2D)getGraphics();
	
	g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	g.clearRect(0, 0, rotatedWidth, rotatedHeight);
	g.setColor(Color.WHITE);
	g.fillRect(0, 0, rotatedWidth, rotatedHeight);
	g.drawImage(rotatedImage, null, null);
}
 
/**
* Get the corresponding coordinates in the source image for the specified
* coordinates in the rotated image using the following transformation
* matrix: | xSrc | = | xRot | | cos(angle) sin(angle) | | | | | * | | |
* ySrc | = | yRot | | -sin(angle) cos(angle) |
*
* @param xRot
* The x ccordinate in the rotated image
* @param yRot
* The y coordinate in the rotated image
*
* @return The corresponding (x,y) coordinate in the source image
*/
private Point getSourceXY(int xRot, int yRot) {
	int dx = xRot;
	int dy = yRot;
	int x = ((int) (dx * Math.cos(angle) + dy * Math.sin(angle))) + (sourceWidth / 2);
	int y = ((int) (dy * Math.cos(angle) - dx * Math.sin(angle))) + (sourceHeight / 2);
	return new Point(x, y);
}
 
 
/**
* Get the angle value
*
* @return The rotation angle value
*/
public double getAngle() {
	return angle;
}
 
/**
* Set the angle value
*
* @param angle
* The rotation angle value
*/
public void setAngle(double angle) {
	this.angle = angle;
	rotate();
}
 
/**
* Get the source image
*
* @return The source image (not rotated)
*/
public BufferedImage getSourceImage() {
	return sourceImage;
}
 
}
